#include "AspectEngine.h"

namespace agtk {
	
Shader::Shader(char* vertFile, char* fragFile)
{
	m_vertexShader = 0;
	m_fragmentShader = 0;
	const char * vertSource = ReadEntireFile(vertFile); // Points to the entirity of the vertex shader source.
	const char * fragSource = ReadEntireFile(fragFile); // Same for the fragment shader
	m_vertexShader = glCreateShader(GL_VERTEX_SHADER); // Creates shader handlers.
	m_fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);	
	glShaderSource(m_vertexShader, 1, &vertSource,NULL); // Loads source code into shader objects.
	glShaderSource(m_fragmentShader, 1, &fragSource,NULL);
	glCompileShader(m_vertexShader); // Compiles the shaders into object code.
	glCompileShader(m_fragmentShader);
	m_shaderProgram = glCreateProgram(); // Create a program object for shaders to be attached to.
	glAttachShader(m_shaderProgram, m_vertexShader); // Attach shaders to the program.
	glAttachShader(m_shaderProgram, m_fragmentShader);
	glLinkProgram(m_shaderProgram); // Link the program. It is now ready to use.

	GLchar* log = NULL;
	GLsizei* length = NULL;

	glGetShaderInfoLog(m_vertexShader, 255, length, log);
	if (length>0)
		std::cout << "Vertex Shader: " << log << std::endl;

	glGetShaderInfoLog(m_fragmentShader, 255, length, log);
	if (length>0)
		std::cout << "Fragment Shader: " << log << std::endl;

	GLenum glError;
	glError = glGetError();
	while (glError != GL_NO_ERROR) {
		glError = glGetError();
		switch (glError) {
			case GL_INVALID_ENUM:
				g_aspectEngine->GetLogger()->Log(LOGGER_ERROR, "Invalid enum.");
				break;
			case GL_INVALID_VALUE:
				g_aspectEngine->GetLogger()->Log(LOGGER_ERROR, "Invalid value.");
				break;
			case GL_INVALID_OPERATION:
				g_aspectEngine->GetLogger()->Log(LOGGER_ERROR, "Invalid operation.");
				break;
			case GL_STACK_OVERFLOW:
				g_aspectEngine->GetLogger()->Log(LOGGER_ERROR, "Stack overflow.");
				break;
			case GL_OUT_OF_MEMORY:
				g_aspectEngine->GetLogger()->Log(LOGGER_ERROR, "Out of memory.");
				break;
			case GL_TABLE_TOO_LARGE:
				g_aspectEngine->GetLogger()->Log(LOGGER_ERROR, "Table too large.");
				break;
		}
		glError = glGetError();
	}
	
	// Log a message.
	std::ostringstream oss (std::ostringstream::out);
	// Put data into string stream like you would with cout of other similar functions.
	oss << vertFile << ", " << fragFile;
	// Log message.
	g_aspectEngine->GetLogger()->Log(LOGGER_INFO, "New shader program ready to rock and roll!", const_cast<char*>(oss.str().c_str()));
}

Shader::~Shader(void)
{
	glDetachShader(m_shaderProgram, m_vertexShader); // Detatch shaders from the program.
	glDetachShader(m_shaderProgram, m_fragmentShader);
	glDeleteShader(m_vertexShader); // Delete the shaders.
	glDeleteShader(m_fragmentShader);
	glDeleteProgram(m_shaderProgram); // Delete the program.
}

void Shader::UseShader(bool use)
{
	// Use defaults to true. Using 'UseShader(false)' will disable any shader.
	if (use) 
		glUseProgram(m_shaderProgram);
	else
		glUseProgram(0); // Unload shader program and return to the fixed function pipeline.
}

UniformShaderParameter* Shader::GetUniform(const char* name)
{
	// Get OpenGL handle.
	GLint glHandle = glGetUniformLocation(m_shaderProgram, name);

	return new UniformShaderParameter(glHandle);
}

ShaderAttribute* Shader::GetAttribute(const char* name)
{
	// Get OpenGL handle.
	GLint glHandle = glGetAttribLocation(m_shaderProgram, name);

	return new ShaderAttribute(glHandle);
}

}